Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDH: add get_secret support for Aliyun KMS #423

Merged
merged 3 commits into from
Jan 19, 2024

Conversation

1570005763
Copy link
Contributor

@1570005763 1570005763 commented Dec 29, 2023

Apply get_secret() function for Getter trait.

And two methods of accessing Aliyun KMS is offered. One is through ClientKey, the same as encrypt/decrypt function, the other is through EcsRamRole, which can only be used on Aliyun ECS. More information is updated to the doc file.

Again it is hard to test code on CI. I can only test them on my machine, this is a screenshot of the test results.
image

@1570005763 1570005763 force-pushed the feat-kms-aliyun-sm branch 4 times, most recently from 2a84202 to 90a64d4 Compare December 29, 2023 04:11
@1570005763
Copy link
Contributor Author

1570005763 commented Dec 29, 2023

The basic build check failure is irrelevant to this PR. I'll open another PR to fix it.

@1570005763
Copy link
Contributor Author

The basic build check failure is fixed in #424.

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @1570005763 for this patch. Here are some comments

@@ -18,12 +18,12 @@ use serde_json::Value;
use sha2::{Digest, Sha256};
use tokio::fs;

use crate::plugins::aliyun::client::dkms_api::{DecryptRequest, EncryptRequest};
// use crate::plugins::aliyun::client::dkms_api;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the note // here for debug?

Copy link
Contributor Author

@1570005763 1570005763 Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted.

"client_key_id": "KAAP.2bc431c0-416f-4ccb-8638-e71245fd60c1",
"kms_instance_id": "kst-bjj65794c36xm0v9aeujs",
});
// init encrypter at user side
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encrypter -> getter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

#[ignore]
#[tokio::test]
async fn get_secret_with_client_key() {
let secret_name = "test_secret";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, rtest looks better.

@@ -21,17 +21,19 @@ log.workspace = true
openssl = { workspace = true, optional = true }
p12 = { version = "0.6.3", optional = true }
prost = { workspace = true, optional = true }
rand.workspace = true
reqwest = { version = "0.11", optional = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we already imported request in the workspace's Cargo.toml, here can be
reqwest = { workspace = true, optional = true }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

Comment on lines 9 to 17
// use base64::{engine::general_purpose::STANDARD, Engine};
// use log::debug;
// use openssl::{
// pkey::{PKey, Private},
// sign::Signer,
// };
// use p12::{CertBag, ContentInfo, MacData, SafeBagKind, PFX};
// use serde::Deserialize;
// use yasna::ASN1Result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these left by debug?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted.

Comment on lines 175 to 178
return Err(anyhow!(
"Request session_credential failed with status: {}",
response.status()
));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Err(anyhow!(
"Request session_credential failed with status: {}",
response.status()
));
bail!(
"Request session_credential failed with status: {}",
response.status()
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

Comment on lines 182 to 184
let session_ak = body_json["AccessKeyId"].as_str().unwrap().to_string();
let session_sk = body_json["AccessKeySecret"].as_str().unwrap().to_string();
let token = body_json["SecurityToken"].as_str().unwrap().to_string();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please avoid using unwrap() but an error handling like ok_or to prevent the process from panic?

Also, if directly use ["SecurityToken"] to index the field in a JSON, it might also panic if no field named SecurityToken exists. Please try to use .get("..") instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Helpful suggestion! Has been modified.

}
}

pub(crate) fn urlencode_openapi(&self, s: &String) -> String {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub(crate) fn urlencode_openapi(&self, s: &String) -> String {
pub(crate) fn urlencode_openapi(&self, s: &str) -> String {

When use a reference to a String, &str is preferred.

BTW, is this function actually used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's used in both build_params() and do_request() functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

client_key_id: ck.key_id.clone(),
private_key,
};

Ok(credential)
}

pub(crate) fn generate_bear_auth(&self, str_to_sign: &str) -> Result<String> {
pub(crate) fn sign(&self, str_to_sign: &str) -> Result<String> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function actually generates bear auth string. Do we need to change the name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was originally hoping to unify the function names for obtaining signatures in clientKey access and ecsRamRole access. It seems that the sign() might be confusing. What do you think about changing it to get_signature()?

@1570005763
Copy link
Contributor Author

I have reorganized the code structure to make it easier to understand. Please take another look. @Xynnn007

Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments

})?;

let ecs_ram_role_name =
if let Some(ecs_ram_role_name) = ecs_ram_role_json["ecs_ram_role_name"].as_str() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the existance of ecs_ram_role_json["ecs_ram_role_name"] before using that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified.

.join("&");
let urlencoded_canonicalized_params: String =
credential.urlencode_openapi(&canonicalized_params);
let string_to_sign = format!("POST&%2F&{}", urlencoded_canonicalized_params,);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let string_to_sign = format!("POST&%2F&{}", urlencoded_canonicalized_params,);
let string_to_sign = format!("POST&%2F&{}", urlencoded_canonicalized_params);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted.

.gitignore Show resolved Hide resolved
thiserror.workspace = true
tokio = { workspace = true, features = ["fs"] }
toml.workspace = true
tonic = { workspace = true, optional = true }
url.workspace = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to make url as optional to aliyun

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified.

@@ -21,17 +21,19 @@ log.workspace = true
openssl = { workspace = true, optional = true }
p12 = { version = "0.6.3", optional = true }
prost = { workspace = true, optional = true }
rand.workspace = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to make this optional to aliyun

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified.


// implement ConfigClientKey related function
impl ConfigClientKey {
pub(crate) fn new(kms_instance_id: &str, endpoint: &str) -> Result<Self> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do not return any Error, usually we do not use Result<Self> but directly Self

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result<_> removed.

@@ -0,0 +1,29 @@
// Copyright (c) 2023 Alibaba Cloud
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2023 -> 2024

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.


// implement ConfigEcsRamRole related function
impl ConfigEcsRamRole {
pub(crate) fn new(region_id: &str, endpoint: &str, vpc: &str) -> Result<Self> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same issue upon Result<_> usage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result<_> removed.

/// Export the [`ProviderSettings`] of the current client. This function is to be used
/// in the encryptor side. The [`ProviderSettings`] will be used to initial a client
/// in the decryptor side.
pub fn export_provider_settings(&self) -> Result<ProviderSettings> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same issue upon Result<_> usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Result<_> removed.

Comment on lines 76 to 109
let inner_client = if provider_settings.contains_key("client_type")
&& provider_settings["client_type"] == "client_key"
{
Client::ClientKey {
client_key_client: ClientKeyClient::from_provider_settings(provider_settings)
.await
.map_err(|e| {
Error::AliyunKmsError(format!(
"build ClientKeyClient with `from_provider_settings()` failed: {e}"
))
})?,
}
} else if provider_settings.contains_key("client_type")
&& provider_settings["client_type"] == "ecs_ram_role"
{
Client::EcsRamRole {
ecs_ram_role_client: EcsRamRoleClient::from_provider_settings(provider_settings)
.await
.map_err(|e| {
Error::AliyunKmsError(format!(
"build EcsRamRoleClient with `from_provider_settings()` failed: {e}"
))
})?,
}
} else {
return Err(Error::AliyunKmsError(
"client type invalid or not exist.".to_string(),
));
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you do some refactoring like the following to make the logic clearer

Suggested change
let inner_client = if provider_settings.contains_key("client_type")
&& provider_settings["client_type"] == "client_key"
{
Client::ClientKey {
client_key_client: ClientKeyClient::from_provider_settings(provider_settings)
.await
.map_err(|e| {
Error::AliyunKmsError(format!(
"build ClientKeyClient with `from_provider_settings()` failed: {e}"
))
})?,
}
} else if provider_settings.contains_key("client_type")
&& provider_settings["client_type"] == "ecs_ram_role"
{
Client::EcsRamRole {
ecs_ram_role_client: EcsRamRoleClient::from_provider_settings(provider_settings)
.await
.map_err(|e| {
Error::AliyunKmsError(format!(
"build EcsRamRoleClient with `from_provider_settings()` failed: {e}"
))
})?,
}
} else {
return Err(Error::AliyunKmsError(
"client type invalid or not exist.".to_string(),
));
};
let Some(client_type) = provider_settings.get("client_type") else {
return Err(Error::AliyunKmsError(
"client type invalid or not exist.".to_string(),
));
};
let inner_client = match client_type {
"client_key" => ...
"ecs_ram_role" => ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! It does look clearer.

@1570005763 1570005763 force-pushed the feat-kms-aliyun-sm branch 2 times, most recently from eb4df0d to 2237a86 Compare January 19, 2024 03:04
Copy link
Member

@Xynnn007 Xynnn007 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

/// [`ALIYUN_IN_GUEST_DEFAULT_KEY_PATH`] which is the by default path where the credential
/// to access kms is saved.
pub async fn from_provider_settings(_provider_settings: &ProviderSettings) -> Result<Self> {
let ecs_ram_role_path = format!("{ALIYUN_IN_GUEST_DEFAULT_KEY_PATH}/ecsRamRole.json");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use concatcp! to declare this as a const.

Signed-off-by: 1570005763 <daniel.duan@linux.alibaba.com>
Signed-off-by: 1570005763 <daniel.duan@linux.alibaba.com>
Signed-off-by: 1570005763 <daniel.duan@linux.alibaba.com>
@Xynnn007 Xynnn007 merged commit 178a689 into confidential-containers:main Jan 19, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants